はじめに
Raspberry Pi に 128x64 OLED (SSD1306) を繋げて Python から文字を表示してみました。私が買ったのは DIYmall というメーカーの これです。たぶん これ とかも同様の手順で動くと思います。
モジュールのインストールとサンプルプログラムの実行
結論と言うか概要から先に書くと以下の Pillow と言う Python のモジュールを使って絵を書いてから以下の Adafruit のモジュールで 128x64 OLED ディスプレイに書き出します。以下のモジュールは既にメンテナンスはされておらず非推奨となっていますが現時点で最新の Raspberry Pi OS 11 (bullseye) と Raspberry Pi 3 の組み合わせで動作することを確認しています。
まずは raspi-config を起動します。
$ sudo raspi-config
次に以下を選択して
3 Interface Options Configure connections to peripherals
以下を選択します。
I5 I2C Enable/disable automatic loading of I2C kernel module
"Would you like the ARM I2C interface to be enabled?" と聞かれるので "Yes" と答えてから raspi-config を終了させてください。
配線はここらへんです。
Pi 3.3V <-> OLED VIN
Pi GND <-> OLED GND
Pi GPIO 2 (SDA) <-> OLED SDA
Pi GPIO 3 (SCL) <-> OLED SCL
次に上記 Adafruit の Python モジュール Adafruit_Python_SSD1306 をインストールします。
$ sudo python3 -m pip install --upgrade pip setuptools wheel
$ sudo pip3 install Adafruit-SSD1306
Adafruit のサンプルプログラムを動かしてみます。
$ git clone https://github.com/adafruit/Adafruit_Python_SSD1306
$ cd ./Adafruit_Python_SSD1306/examples/
$ pytnon3 ./stats.py
OLED ディスプレイに IP アドレスなどの情報が表示されます。デフォルトでは解像度が 128x32 となっているので stats.py の 46 行目から 50 行目を次のように変更し 128x64 表示とできます。
# 128x32 display with hardware I2C:
# disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST)
# 128x64 display with hardware I2C:
disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)
他のサンプルプログラムでは猫の画像が表示されたり (image.py) 文字がアニメーションで動いたり (animate.py) します。
単に 3 行の文字を表示する
日本語を扱うためにオープンソースの源ノ角ゴシックフォントを Python プログラムと同じディレクトリにダウンロードします。
$ wget https://github.com/adobe-fonts/source-han-sans/raw/release/SubsetOTF/JP/SourceHanSansJP-Medium.otf
プログラムは以下のような感じのものを strings.py として書きました。
import Adafruit_SSD1306
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
def initialize_display():
# Raspberry Pi pin configuration is "NONE" as it isn't used
RST = None
# 128x64 display with hardware I2C
disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)
# Initialize and clear display
disp.begin()
disp.clear()
disp.display()
return(disp)
def initialize_image(disp):
# Create a blank image object for drawing by using Pillow with 1-bit color.
width = disp.width
height = disp.height
image = Image.new('1', (width, height))
draw = ImageDraw.Draw(image)
return(image, draw)
def write_image(disp, draw, image):
top = -2
x = 0
# Write three lines of text.
draw.text((x, top + 0), 'こんにちは', font=font, fill=255)
draw.text((x, top + 20), '1234567890', font=font, fill=255)
draw.text((x, top + 40), 'Hello, World!', font=font, fill=255)
disp.image(image)
disp.display()
if __name__ == "__main__":
# Load ORT font
font = ImageFont.truetype('SourceHanSansJP-Medium.otf', 16)
disp = initialize_display()
image, draw = initialize_image(disp)
write_image(disp, draw, image)
次のように実行します。
$ python3 ./string.py
画面に
こんにちは
0123456878
Hello, World!
と表示されれば成功です。数字が8までしか表示されていないのでフォントサイズが16だと1行で9文字までのようです。
補足
上記 Phtnon のプログラムでは Pillow という画像処理ライブラリで初期化を行い (initialize_image) 128x64 のイメージを作ってから OLED ディスプレイに書き出しています (write_image)。
プログラムの中で GPIO の番号が指定されていませんが I2C という通信方式は。こことかここ とか ここ などが参考になりましたが、Raspberry Pi では I2C_SDA は GPIO 2 で I2C_SCL は GPIO 3 となるようです。
I2C はマスター・スレーブ構成になっており Raspberry Pi (マスター) からスレーブ (OLED ディスプレイ) が見えているかは i2cdetect コマンドを使って確認ができます。 3c などのスレーブのアドレスが見えていれば正常です。
$ i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
フォントは好きなものが使えます。英数字だと dafont.com が元のサンプルコードで紹介されていました。
日本語フォントも源ノ角ゴシックやIPAexフォントをはじめ様々な種類のオープンソースのフォントから選ぶことができます。ただ調べきれてませんが Pillow はバリアブルフォントなどには対応していないようで対応しているフォントを使用する必要があるようです。 それにしても昔の Linux デスクトップ環境で日本語の TrueType フォントに苦労した時代とはえらい差です。
おまけ
もっと良い関数の分け方 (あるいは Class の使い方) があるかもしれませんが、write_image を何度も呼べば表示されている文字を変えることができます。以下のプログラムでは「こん」「にち」「は」「世界」という文字を 1 秒毎に書き換えるプログラムです。stats.py でも良いですが Raspberry Pi で得たなんらかの情報を表示するのに使えそうです。
import time
import Adafruit_SSD1306
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
def initialize_display():
# Raspberry Pi pin configuration is "NONE" as it isn't used
RST = None
# 128x64 display with hardware I2C
disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)
# Initialize and clear display
disp.begin()
disp.clear()
disp.display()
return(disp)
def initialize_image(disp):
# Create blank image object for drawing by using Pillow with 1-bit color.
width = disp.width
height = disp.height
image = Image.new('1', (width, height))
draw = ImageDraw.Draw(image)
return(image, draw)
def write_image(disp, draw, image, string):
top = -2
x = 0
# Write three lines of text.
draw.text((x, top + 0), string, font=font, fill=255)
disp.image(image)
disp.display()
if __name__ == "__main__":
# Load ORT font
font = ImageFont.truetype('SourceHanSansJP-Medium.otf', 48)
disp = initialize_display()
image, draw = initialize_image(disp)
strings = ["こん", "にち", "は", "世界"]
while True:
try:
for string in strings:
# clear the display by writing a black rectangle
draw.rectangle((0,0,disp.width,disp.height), outline=0, fill=0)
write_image(disp, draw, image, string)
time.sleep(1)
except KeyboardInterrupt:
break