ゆるエンジニアはいろいろ遊びたい

FAエンジニアが週末にいろいろ遊ぶブログです

Raspberry Pi5 でOLEDディスプレイを使う

ラズパイ5でOLEDディスプレイを使ってみました。

RUIZHI 0.96インチI2C 128×64 SSD1315

3個で¥1,200くらいでアマゾンで購入。

配線は以下の通り

VCC:1番ピン(3.3V)

GND:9番ピン(0V)

SDA:3番ピン(SDA)

SCL:5番ピン(SCL)

まずはモジュールをインストールします。

仮想環境を作成する際に、

python -m venv venv --system-site-packages

で作成します。GPIOZEROを使用するときと同じですね。

そして、仮想環境下でモジュールをインストールします。

pip install adafruit-circuitpython-ssd1306 pillow

コードはこちら

import board
import busio
import adafruit_ssd1306
from PIL import Image, ImageDraw, ImageFont


i2c = busio.I2C(board.SCL, board.SDA)

WIDTH = 128
HEIGHT = 64
oled = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c, addr=0x3C)

try:
    oled.fill(0)
    oled.show()

    image = Image.new("1", (WIDTH, HEIGHT))
    draw = ImageDraw.Draw(image)

    draw.text((0, 0), "OLED OK!", fill=255)
    oled.image(image)
    oled.show()

    print("Display test completed successfully.")
    input("Press Enter to clear the display and exit...")    

finally:
    oled.fill(0)
    oled.show()

実行すると文字が表示されました。
ENTERで終了します。終了時に何もしないと表示が残ったままで気持ち悪いので、最後に塗りつぶしています。

今回かなりハマってしまい、実行すると「OSError: [Errno 121] Remote I/O error」が出てしまい、何も表示されないトラブルが発生してしまいました。
ChatGPTにいろいろ質問すると、配線は問題なくI2C通信がうまくいってないからだと言われ、いろいろ対策を考えてもらいましたが何をしても直りません。
しまいにはOSがTrixieだからだと言われる始末。
結局ディスプレイを交換したところあっさり表示され、ハードウェアの故障という事が判明しました。
今回購入した3個のうち2個が故障していて、非常にガッカリしました。この辺はアマゾンで購入したのが間違いだったのでしょう。
表示できる事が確認されたので、CPU情報を表示してみました。

import time
import subprocess
import psutil
import board
import busio
import adafruit_ssd1306
from PIL import Image, ImageDraw, ImageFont
import atexit


# =========================
# OLED 初期化
# =========================
i2c = busio.I2C(board.SCL, board.SDA)

WIDTH = 128
HEIGHT = 32
oled = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c, addr=0x3C)

oled.fill(0)
oled.show()

def _clear_on_exit():
    oled.fill(0)
    oled.show()
atexit.register(_clear_on_exit)

# 描画用
image = Image.new("1", (WIDTH, HEIGHT))
draw = ImageDraw.Draw(image)

# =========================
# 情報取得関数
# =========================
def get_cpu_temp():
    with open("/sys/class/thermal/thermal_zone0/temp") as f:
        return float(f.read()) / 1000.0

def get_cpu_freq():
    with open("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq") as f:
        return float(f.read()) / 1_000_000

def get_gpu_temp():
    try:
        out = subprocess.check_output(["vcgencmd", "measure_temp"]).decode()
        return float(out.split('=')[1].split("'")[0])
    except:
        return 0.0

def get_gpu_freq():
    try:
        out = subprocess.check_output(["vcgencmd", "measure_clock", "core"]).decode()
        return float(out.split('=')[1]) / 1_000_000
    except:
        return 0.0

# =========================
# メイン表示ループ
# =========================
try:
    while True:
        cpu_f = get_cpu_freq()
        cpu_t = get_cpu_temp()
        gpu_f = get_gpu_freq()
        gpu_t = get_gpu_temp()
        load = psutil.cpu_percent()

        draw.rectangle((0, 0, WIDTH, HEIGHT), outline=0, fill=0)

        draw.text((0, 0), f"CPU {cpu_f:.2f}GHz {cpu_t:.1f}C", fill=255)
        draw.text((0, 10), f"Load {load:.0f}%", fill=255)
        draw.text((0, 20), f"GPU {int(gpu_f)}MHz {gpu_t:.1f}C", fill=255)

        oled.image(image)
        oled.show()

        time.sleep(0.5)

except KeyboardInterrupt:
    oled.fill(0)
    oled.show()

0.5秒ごとに情報を更新しています。負荷がかかると数字が上がってきてテンションも上がりますね。
もっとラズパイを動かしてあげたくなります。