Raspberry Pi + 8×8 LED マトリックスで日本語文字列をスクロール表示する

2016/8/13追記:

本記事では色々試行錯誤した過程をそのまま載せてしまっているので、「8x8LED マトリックスで日本語文字列をスクロール表示させるためのポイントだけ知りたい!」という方は、こちらの記事の後半をご確認ください。

 

以下、元記事です。

 

ラピロへの組み込みに向けて、Raspberry Piで8×8 LED マトリックスを使えるようにしたいと思います。今回使うのはこれです。

私は赤色を使いますが、他の色もあります。

まずはハード側の準備から。マトリックス基板のはんだ付けについては、こちらを参照してください。

接続はちょっと見にくいですが、こんな感じです。

th_led_matrix_1

Raspberry Piの3.3V, SDA, SCL, GNDからブレッドボード(*小型ブレッドボードの切れっ端)に導線を挿して、複数のI2Cデバイスを接続しやすいようにしています。ちょっとごちゃごちゃしていますが。

次に、ソフト側の設定として、I2Cをセットアップします(*正しくはI2Cですが、入力が面倒なので、以下もI2Cと表記します)。

(2015/6/13追記:以下のI2C有効化の手順は古くなっています。2015/1/31リリース以降のRaspbian OSを利用されている方は、例えばこちらの方の記事などを参考にしてI2Cを有効化してください)

“/etc/modules”ファイルの末尾に、以下の2文を記述します。

i2c-bcm2701
i2c-dev

“i2c-dev”だけで良いのかもしれませんが、一応。それから、”/etc/modprove.d/raspi-blacklist.conf”の以下の行をコメントアウトします。

blacklist i2c-bcm2708

それから、必要なパッケージをインストールします。

$ sudo apt-get install i2c-tools

python用のライブラリもインストールしておきます。

$ sudo apt-get install python-smbus

ここまでできたら、再起動します。

$ sudo reboot

I2Cデバイスを繋いでいることを確認したら、I2Cアドレスを確認してみます。

pi@raspberrypi ~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: 70 -- -- -- -- -- -- --

アドレス0x70が表示されているので、大丈夫そうです。

ではでは、ここからLEDマトリックスの制御を始めようと思います。Raspberry PiでのLEDマトリックスの制御については、ここに情報があります。製造元のAdafruitがGituhub上にライブラリを用意してくれているので、それを落としてきます。

Raspberry Piにgitがインストールされていないなら、まずはgitをインストールします。

$ sudo apt-get install git

gitが使えるようになったら、ライブラリを落としてきます。

$ git clone https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code.git

成功すれば、”Adafruit-Raspberry-Pi-Phtyon-Code”ディレクトリができているはずです。ディレクトリを移動して、動作テストをします。

$ cd Adafruit-Raspberry-Pi-Python-Code/Adafruit_LEDBackpack
$ sudo python ex_8x8_pixels.py

こんな感じでLEDが点灯しました。

th_led_matrix_2

pythonは詳しくないのでよくわかりませんが、とりあえず以下のファイルを同じフォルダにおいておけば任意のフォルダでサンプルを実行可能です。

  • Adafruit_8x8.py
  • Adafruit_8x8.pyc
  • Adafruit_I2C.py
  • Adafruit_i2C.pyc
  • Adafruit_LEDBackpack.py
  • Adafruit_LEDBackpack.pyc
  • ex_8x8_pixels.py

ということで、ex_8x8_pixel.pyをベースに、色々ソースに手を加えてみようと思います。

 

いきなりですが、LEDマトリックスをもう一つ増やしてみます。同じものをそのまま接続するとI2Cアドレスが競合するので、片方はショートさせてアドレスを変更します。

 th_led_matrix_3

赤丸の部分のショートのさせ方で、4つのアドレスを使い分けられるようになっています。ちなみに、スイッチサイエンス等で買うとオス型のピンヘッダが同封されていますが、個人的にはメス型の方が使いやすかったので、自前のメス型(ソケット)を取り付けています。

とりあえず配線します。

 th_led_matrix_4

より一層ごちゃごちゃしてきました。後日、綺麗に配線しなおします。

二つのI2Cデバイスが認識されているかを確認します。

pi@raspberrypi ~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: 70 71 -- -- -- -- -- --

I2Cアドレスがもう一つ増えたので、大丈夫そうです。

“ex_8x8_pixels.py”に、もう一つアドレスを付け加えます。

#!/usr/bin/python

import time
import datetime
from Adafruit_8x8 import EightByEight

# ===========================================================================
# 8x8 Pixel Example
# ===========================================================================
grid_l = EightByEight(address=0x70)
grid_r = EightByEight(address=0x71)

print "Press CTRL+Z to exit"

# Continually update the 8x8 display one pixel at a time
while(True):
  for x in range(0, 8):
    for y in range(0, 8):
      grid_l.setPixel(x, y)
      grid_r.setPixel(x, y)
      time.sleep(0.05)
  time.sleep(0.5)
  grid_l.clear()
  grid_r.clear()
  time.sleep(0.5)

 th_led_matrix_5

うまいこといきました。あとはどう表示させるかです。

できれば図形や文字を簡単に表示できるようにしたいな、ということでライブラリを探してみると、さっきのとは別のライブラリをAdafruitが提供しているようなので、それを入れてみます。

“build-essential python-dev python-smbus python-imaging git”の5つのパッケージが必要らしいので、まずは足りていないパッケージを入れます。すでに入っているパッケージは、以下のコマンドで確認できます。

$ dpkg -l

自分の場合は”python-dev python-imaging”がなかったので、これらを入れます。

$ sudo apt-get update
$ sudo apt-get install python-dev python-imaging

次に、gitのcloneでライブラリを落としてきて、ライブラリをインストールします。

$ git clone https://github.com/adafruit/Adafruit_Python_LED_Backpack.git
$ cd Adafruit_Python_LED_Backpack
$ sudo python setup.py install

完了したら、テストです。

$ cd examples
$ sudo python matrix8x8_test.py

点が移動していって、こんなマークが表示されました。

th_led_matrix_6

さらっとソースを見ましたが、このライブラリを使えば、マル・バツ・三角・四角ぐらいなら簡単に描画できそうです。ライブラリはRaspberry Pi本体にインストールしているので、”matrix8x8_test.py”の実行には他のファイルは特に必要ありません(先ほど持ってきたAdafruit_xxx.py(pyc)も不要)。

さらに欲を出して、文字列の表示までさせたいと思います。さきほどの”matrix8x8_test.py”の末尾に、「テキストの表示がしたかったら、SSD1306 libraryのexamplesを見てね」と書いてあるので、これまでと同じようにライブラリを引っ張ってきます。

(注:以下にやり方を書いてはいますが、このライブラリのインストールはやらなくてもいいかもしれません。理由は後述します)

$ git clone https://github.com/adafruit/Adafruit_Python_SSD1306

ここの使い方を見てみると、このライブラリにも色々必要なパッケージやら何やらがあるようですが、自分の環境にはすでに全部入っていたので、後はライブラリをRaspberry Pi本体にインストールします。

$ cd Adafruit_Python_SSD1306
$ sudo python setup.py install

で、サンプルを実行してみます。

$ cd examples
$ sudo python animate.py

これは実行エラーになります。ソースを見てみると、どうも8×8 LEDマトリックスには対応していない模様。

「?」となったので、もう一度ライブラリの使い方を見てみると、サンプルソースには以下のコードが含まれているとのこと。

# Load default font.
font = ImageFont.load_default()

# Alternatively load a TTF font.
# Some other nice fonts to try: http://www.dafont.com/bitmap.php
#font = ImageFont.truetype('Minecraftia.ttf', 8)

# Write two lines of text.
draw.text((x, top),    'Hello',  font=font, fill=255)
draw.text((x, top+20), 'World!', font=font, fill=255)

drawはImageDrawのオブジェクトです。先ほどの”matrix8x8_test.py”も、ImageDrawを使ってLEDに表示する図形を描画していました。ということは、同じようにすれば文字表示もいける?ライブラリはインストールする必要はなくて、「ここに書いてある”draw.text()”の使い方を見てね」ということだったのか。

というわけで、”matrix8x8_test.py”を改良していきます。途中色々やりますが、最終的に完成したソースコードだけ最後に載せます。

最初に”import ImageFont”を追加して、”He”と表示させようとすると、

 th_led_matrix_7

こんな感じになりました。フォントのサイズが合っていなくて、一部が表示されているようです。

ImageFontのリファレンスを見てみると、”ImageFont.load_default()”ではなく、”ImageFont.truetype(…)”でフォントをロードするようにすれば、サイズを指定できる模様。

ということで、8×8で良さげなフォントないかしら、と探してみたら、うってつけのものがありました。

「最初はとりあえず英数字が出せればいいかなー」ぐらいに思っていたので、これは嬉しい誤算です。さっそく使わせていただきます。TrueType形式をダウンロードして、ゴシック体(misaki_gothic.tff)を参照するようにします。ただ、プログラムで日本語を扱うのに、ちょっとだけ修正が必要になります。以下を参考にさせていただきました。

というわけで、さっそく修正して実行してみたものがこちら。

th_led_matrix_8

ちゃんと「He」として読めそうです。

th_led_matrix_9

シンプルな漢字なら、こんな感じでバッチリです。さすがに複雑な漢字だと、一文字出されただけでは認識が難しいですが、並べて文字列にすると、人はちゃんと意味を認識できるようです(こちらをご参照ください)。

さて、どうにか文字の表示まではできるようになったので、あとはできればスクロールするようにもっていきたいところです。

事例を探していたら、どうやら先にインストールした”Adafruit_Python_LED_Backpack”ライブラリに画像のスクロール機能を載せようとしているようで、リリースにはなっていません(2015/01/17時点)が、ソースコード自体はこちらに存在しています。

ということで、インストールしたライブラリ”Adafruit_Python_LED_Backpack/Adafruit_LED_Backpak/Matrix8x8.py”を、上記のリンク先の”Matrix8x8.py”に差し替えて、ライブラリをインストールし直します。

$ cd Adafruit_Python_LED_Backpack
$ sudo python setup.py install

これで、8×8 LEDマトリックス基板での文字列スクロールを簡単に記述できるようになりますが、マトリックス基板を2つ繋げてスクロールさせたい場合には、もう一工夫必要です。

ライブラリファイル”Matirx8x8.py”に、以下の関数を追加します。

def horizontal_multi_scroll(self, image, total=1, number=0):

    image_list = list()
    width = image.size[0]

    for x in range(8 * number):
      display_section = self.create_blank_image()
      image_list.append(display_section)

    # Scroll into the blank image.
    for x in range(8):
      section = image.crop((0, 0, x, 8))
      display_section = self.create_blank_image()
      display_section.paste(section, (8 - x, 0, 8, 8))
      image_list.append(display_section)

    #Scroll across the input image.
    for x in range(8, width + 1):
      section = image.crop((x - 8, 0, x, 8))
      display_section = self.create_blank_image()
      display_section.paste(section, (0, 0, 8, 8))
      image_list.append(display_section)

    #Scroll out, leaving the blank image.
    for x in range(width - 7, width + 1):
      section = image.crop((x, 0, width, 8))
      display_section = self.create_blank_image()
      display_section.paste(section, (0, 0, 7 - (x - (width - 7)), 8))
      image_list.append(display_section)

    for x in range(8 * (total - (number + 1))):
      display_section = self.create_blank_image()
      image_list.append(display_section)

    #Return the list of images created
    return image_list

元々ライブラリに記載されている”horizontal_scroll()”関数にちょっと手を加えたものです。利用するLEDマトリックス基板の数(total)と、向かって右から何番目の基板であるか(number, 0スタート)を引数に与えることで、各マトリックス基板ごとに、文字列を流す前後に追加する空白画像の数を調整します。これで、複数のLEDマトリックスを同時に再生開始すると、文字が流れるように表示されます。

“Matirx8x8.py”に関数を追加したら、再度インストールします。

$ cd ..
$ sudo python setup.py install

ここまでできたら、最後に任意のフォルダでpythonの実行ファイルを書きます。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time

import Image
import ImageDraw
import ImageFont

from Adafruit_LED_Backpack import Matrix8x8

display_0 = Matrix8x8.Matrix8x8(address=0x70, busnum=1)
display_1 = Matrix8x8.Matrix8x8(address=0x71, busnum=1)

display_0.begin()
display_1.begin()

display_0.clear()
display_1.clear()

text = u"OK! わかった! 了解!"
text_len = len(text)
image = Image.new('1', (text_len*8, 8))
draw  = ImageDraw.Draw(image)
font  = ImageFont.truetype("/home/pi/fonts/misaki_gothic.ttf", 8, encoding='unic')
draw.text((0,1), text, font=font, fill=255)

image_list_0 = display_0.horizontal_multi_scroll(image, 2, 0)
image_list_1 = display_1.horizontal_multi_scroll(image, 2, 1)

max_images = len(image_list_0)

for i in range(0, max_images):
  display_0.set_image(image_list_0[i])
  display_1.set_image(image_list_1[i])
  display_0.write_display()
  display_1.write_display()
  time.sleep(0.10)

書けたら、実行します。

$ sudo python matrix8x8_scroll.py

実行結果は、このようになります。

とりあえず、目標達成です。おつかれさまでした。