Raspberry Pi4B OLEDディスプレイ(I2C接続)を使う

OSとか使うもの

OLEDは以下を使用。制御チップが SSD1306 とポピュラーなものなのでいろいろ楽。

GeeekPi 2個 OLEDディスプレイモジュール I2C IIC 128X64ピクセル 0.96インチ ディスプレイモジュール 黄 青 2色ディスプレイ Raspberry Pi Arduino 51シリーズ MCU STM32 R3およびMegaに対応
Amazon.co.jp: GeeekPi 2個 OLEDディスプレイモジュール I2C IIC 128X64ピクセル 0.96インチ ディスプレイモジュール 黄 青 2色ディスプレイ Raspberry Pi Arduino 51シリーズ...

SPIでつなげるのもありだけど、表示速度は全く必要ないのでI2Cでも十分。
使いピン数もI2Cなら4本と少なくて配線も楽だし。

OS は Ubuntu 24.04

ピンアサインの確認

ラズパイ側

というか、Amazonに記載されている図、そのままでOKだね。

で、この手のOLED、ピン配列が似たり寄ったりなんだけど、ものによって VCC と GNC が逆になっていて、他はそのまま同じ、ということがある。
I2C だけじゃなく SPI な OLED も同じ。
なので、「動かない」とか「I2C のアドレスが表示されない」とかの場合、意外と VCC と GND の逆接してるってのが原因なことがあったりする。
油断せず、ちゃんとピンアサインは確認したほうがいい…

ラズパイの準備

基本的に、メーカーサイトで全部解説されてる。

S-0018

けど、Ubuntuを使ってる場合、Pythonやライブラリのインストール後からうまくいかない。
そこからちょっと別の作業が入るので注意。

I2C の有効化

$ sudo raspi-config

3.Interface Options
I5 I2C

と辿って I2C を有効にする。

あ、Ubuntu を使ってると raspi-config が使えないので、以下のファイルを直接編集する。

/boot/firmware/config.txt

この中にいかがあればOK。なければ追加。

[all]
dtparam=i2c_arm=on

[pi4]
dtoverlay=i2c1

書き換えたらラズパイのリブートを忘れずに。
電源切って、ここでOLED繋げておくと良い。

I2C ツールのインストール

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt install i2c-tools

OLED が既に接続されていれば以下のコマンドで OLED のアドレスがわかるはず。

$ 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: -- -- -- -- -- -- -- --

余談だけど、Raspberry Pi4B だと I2C、4つも使えるのね…

$ i2cdetect -l
i2c-0   i2c             i2c-22-mux (chan_id 0)                  I2C adapter
i2c-1   i2c             bcm2835 (i2c@7e804000)                  I2C adapter
i2c-10  i2c             i2c-22-mux (chan_id 1)                  I2C adapter
i2c-22  i2c             bcm2835 (i2c@7e205000)                  I2C adapter

Python や必要なライブラリをごっそりインストール & 仮想環境構築

次のコマンド、複数行に分かれて表示されていると思うけど、1行で書いて実行。

$ sudo apt -y install python3 python3-pip python3-pil libjpeg-dev zlib1g-dev libfreetype6-dev liblcms2-dev libopenjp2-7 libtiff5

続いて、Python を実行するユーザーをgpioとi2cグループに所属させる。

$ sudo usermod -a -G gpio,i2c your_user_name

ちゃんと追加できたか確認。

$ groups
your_user_name …………… gpio i2c ……………

次に、実行ファイル等を保存しておく場所を作る。
この保存場所はそのまま Python の仮想環境とするので、場所は…

/home/your_user_name/project_python/project_name

みたいな感じがいいかな?(まぁ、お好きなように)
project_nameはディレクトリ名であり、仮想環境の名称にもなる。
project_python ディレクトリに移動して以下コマンドを打てば、仮想環境が構築される。

$ python3 -m venv project_name
$ cd project_name

そのまま次の作業に入りたいけど、ここからは Python の実行環境を整えることになるので、作った仮想環境をアクティベートした上で続ける。(要は仮想環境内に Python 環境を作っていく感じ)

アクティベートは仮想環境のディレクトリにいる場合は下記と打てばよい。
先頭に「.」(ドット)があって、そのあとに半角スペースがあって、「./bin/activate」と続く。

$ . ./bin/activate
(project_name) $

プロンプトの先頭に()付きで仮想環境名(ディレクトリ名)が表示されていれば、仮想環境がちゃんとアクティベートされていることになる。

仮想環境から抜ける=ディアクティベートは以下の通り。

$ deactivate

さて、Python や いくつかのライブラリがインストールできて仮想環境もアクティベートしたので、
続いては Luma をインストールしていく。

ここらあたりからが、Raspberry Pi OS ではなく Ubuntu を使う場合に必要な手順になるかな。
そして、この辺りも詳しくは以下URLを見た方が間違いない。
https://luma-oled.readthedocs.io/en/latest/software.html

まぁ、ともかく、luma.oled をインストールしていこう。

(project_name)$ ./bin/python -m pip install --upgrade luma.oled

python コマンドを使うけど、ここでは仮想環境内の python を明示したほうがいい。
相対パスよりも絶対パスで /home/your_user_name/project_python/project_name/bin/python と書いた方が確実かな。

以上で環境構築終了。
あとはコードを書くだけ。(とはいえ、先ずはサンプルで表示されるかどうかを確認したほうがいいね)

サンプルコード

from luma.core.interface.serial import i2c
from luma.core.render import canvas
from luma.oled.device import ssd1306
import time
 
serial = i2c(port=1, address=0x3C)
device = ssd1306(serial)
 
with canvas(device) as draw:
    draw.rectangle(device.bounding_box, outline="white", fill="black")
    draw.text((30, 40), "Hello World", fill="white")
 
time.sleep(10)

これで Hello World が表示される…はず。

当初、各サイトのサンプルスクリプトを真似て実行してたんだけど、最後の time.sleep(10) を入れなかったため「あれ?表示されない…」と悩んだ。
よく見てると一瞬だけ何か描画されるから「なぜ?」と思ったけど、luma.oled ではスクリプトが終了するときにデバイスを初期化するのだそうだ。
そのため、スクリプトがすぐに終了しないように sleep を入れる必要があった。

まぁ、この辺りは while 等でループさせるなりでスクリプトが終了しないようにすればOKかな。

スクリプトが終了しても消えないようにしたい

一応、ループやスリープ以外にもこんな手段があるらしい。
確かに消えない。

device = ssd1306(serial)
device.cleanup = lambda: None

ただ、以下のドキュメントを読む限り、OLEDディスプレイの寿命延長に寄与したりリソース開放したりするので、できれば無効化しない方が良いかもしれない。

API Documentation — Luma.OLED: Display drivers for SSD1306, SSD1309, SSD1322, SSD1362, SSD1322_NHD, SSD1325, SSD1327, SSD1331, SSD1351, SH1106, SH1107, WS0010, WINSTAR_WEH 3.13.0 documentation

…OLED、0.96インチなんて、1個当たり500円でお釣りがくる安さだから、この設定にして使ってしまっても個人的には問題ないと思うけど…

もしかしたら必要かもしれない作業:GPIOのアクセス権変更

「スクリプトを root で動かしなさい!」みたいなエラーが出ることがある。
特に GPIO を使う場合、権限が足りなくてアクセスできない場合がある。

そんな時は GPIO デバイスへのアクセス権限を確認しよう。

うちの場合は以下のようになっていた。

$ ls -l /dev/gpiomem
crw------- 1 root root 239, 0 Jun  4 21:24 /dev/gpiomem

これだと root 以外には誰も GPIO の様子を知ることができない…
ので、以下のようにして gpio グループにアクセス権を与えてやる。

$ sudo chown root:gpio /dev/gpiomem
$ sudo chmod 660 /dev/gpiomem

この結果、以下のように権限が変更される。

$ ls -l /dev/gpiomem
crw-rw---- 1 root gpio 239, 0 Jun  4 21:24 /dev/gpiomem

この記事にコメントしてみる