ディスプレイを使って、時刻表示してみたいと思っていましたので、

128×64ドット有機ELディスプレイを購入して、家に転がっているラズパイ3を使ってみました。

準備

半田で付けるもの楽しいですが、今回は

  • ブレッドボード
  • ジャンパーワイヤー
  • 0.96インチ 128×64ドット有機ELディスプレイ(OLED) 白色

を秋月電子で購入してきました~。1300円近くで購入

なんとなく、マイクロサーボも購入しました。

実装品を購入すれば、半田ごて要らずでできるのがいいですね。

接続

ネットを探しても、いいコードが見つからないのでどうしようかと思っていました。そこで今流行りのAIに聞いてコード書いてもらいました。便利ですがやはり間違って記述しているのもあります。ので調べています。まずは、日付時刻の表示がしたいので、聞きました。AI便利

import time
from PIL import Image, ImageDraw, ImageFont
from adafruit_ssd1306 import SSD1306_I2C
from board import I2C
import datetime

# OLEDディスプレイのサイズ (例: 128x64)
WIDTH = 128
HEIGHT = 64

# I2Cアドレス (通常は0x3Cですが、異なる場合もあります。確認してください。)
I2C_ADDR = 0x3C

try:
    i2c = I2C()  # 引数なしで I2C インスタンスを作成
    display = SSD1306_I2C(WIDTH, HEIGHT, i2c)
except ValueError as e:
    print("I2Cデバイスが見つかりません。アドレスを確認してください:", e)
    exit()

# ディスプレイを初期化 (begin() は不要)
# display.begin()  # この行は削除

# 画面をクリアするために fill() メソッドを使用
display.fill(0) # 全て黒で塗りつぶす

# フォントのロード (必要に応じて変更)
#font = ImageFont.truetype("/usr/share/fonts/truetype/freefont/FreeSerif.ttf", 12)  # freefont を使用、サイズを指定
font = ImageFont.truetype("/home/pi/UビューRdisplay.ttf", 24)

# 画像オブジェクトを作成
image = Image.new("1", (WIDTH, HEIGHT))
draw = ImageDraw.Draw(image)

def display_time():
    """現在の時刻をディスプレイに表示する関数"""
    now = datetime.datetime.now()
    time_string = now.strftime("%Y-%m-%d %H:%M:%S")  # 年-月-日 時:分:秒 の形式で表示

    draw.rectangle((0, 0, WIDTH, HEIGHT), fill=0) # 画面をクリア
    draw.text((0, 0), time_string, font=font, fill=255)  # 白で描画
    display.image(image)
    display.show()

try:
    while True:
        display_time()
        time.sleep(1)  # 1秒ごとに更新
except KeyboardInterrupt:
    print("プログラムを終了します。")
    display.fill(0) # 画面をクリアして終了
    display.show()

こんな感じ。Radeonのグラボですので、AIに不向きという残念ではありますが、(本当は画像生成をして欲しかったのですが、どうしても無理)文字生成くらいなら余裕!そんなことはどうでもよくて時計だけだと寂しいので温湿度をも欲しいので、AIに聞きました。

前提として

ラズパイ4で温度計を計測してまして、たまに見てます。

ADT7410使用 高精度・高分解能 I2C・16Bit 温度センサモジュール

これおすすめ。ネットで探せばあります。

ラズパイ4からラズパイ3にデータ送信する必要あり。

コード

これもAI聞けばいいの。

ディスプレイ側

import time
from PIL import Image, ImageDraw, ImageFont
from adafruit_ssd1306 import SSD1306_I2C
from board import I2C
import datetime
import psutil  # CPU使用率を取得するために追加
import socket # 温度と湿度を受信するのに必要

# OLEDディスプレイのサイズ (例: 128x64)
WIDTH = 128
HEIGHT = 64

# I2Cアドレス (通常は0x3Cですが、異なる場合もあります。確認してください。)
I2C_ADDR = 0x3C

try:
    i2c = I2C()  # 引数なしで I2C インスタンスを作成
    display = SSD1306_I2C(WIDTH, HEIGHT, i2c)
except ValueError as e:
    print("I2Cデバイスが見つかりません。アドレスを確認してください:", e)
    exit()

# ディスプレイを初期化 (begin() は不要)
# display.begin()  # この行は削除

# 画面をクリアするために fill() メソッドを使用
display.fill(0) # 全て黒で塗りつぶす

# フォントのロード (必要に応じて変更)
font = ImageFont.truetype("/home/pi/UビューRdisplay.ttf", 14)

# 画像オブジェクトを作成
image = Image.new("1", (WIDTH, HEIGHT))
draw = ImageDraw.Draw(image)

HOST = '192.168.1.27'  # サーバーのIPアドレス
PORT = 5000        # サーバーのポート番号


def display_datetime_and_temp_humidity():
    """現在の日付と時刻、温度と湿度をディスプレイに表示する関数"""
    now = datetime.datetime.now()
    date_string = now.strftime("%Y年%m月%d日")  # 年-月-日 の形式で表示
    time_string = now.strftime("%H時%M分%S秒")  # 時:分:秒 の形式で表示

    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
#            s.settimeout(5)
            s.connect((HOST, PORT))
            # Keep-Alive を有効にする (オプション)
#            s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
#            s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 30) #アイドル状態の最大時間(秒)
#            s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 5)  #プローブ間隔(秒)
#            s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3)   #プローブ失敗時の再試行回数
            data = s.recv(1024)
            temperature, humidity = map(float, data.decode().split(','))
            temp_string = f"Temp: {temperature:.1f}°C"
            hum_string = f"Hum: {humidity:.1f}%"

    except (socket.error, ValueError):
        temp_string = "N/A"
        hum_string = "N/A"

    cpu_usage = psutil.cpu_percent(interval=1)  # 1秒間のCPU使用率を取得
    cpu_string = f"CPU: {cpu_usage}%"

    draw.rectangle((0, 0, WIDTH, HEIGHT), fill=0) # 画面をクリア
    draw.text((0, 0), date_string, font=font, fill=255)  # 白で描画 (日付)
    draw.text((0, 15), time_string, font=font, fill=255) # 白で描画 (時刻)
    draw.text((0, 35), temp_string, font=font, fill=255)  # 温度を表示
    draw.text((0, 50), hum_string, font=font, fill=255)  # 湿度を表示

    display.image(image)
    display.show()


try:
    while True:
        display_datetime_and_temp_humidity()
        time.sleep(1)  # 1秒ごとに更新
except KeyboardInterrupt:
    print("プログラムを終了します。")
    display.fill(0) # 画面をクリアして終了
    display.show()

温湿度送る側

import smbus
import time
import socket

# SHT31の設定
bus = smbus.SMBus(1)
address_sht31 = 0x45

def read_sht31():
    """SHT31から温度と湿度を読み取る関数"""
    bus.write_byte_data(address_sht31, 0x24, 0x00)
    time.sleep(0.1)
    readData = bus.read_i2c_block_data(address_sht31, 0x00, 6)

    temp = 0.00267 * ((readData[0] << 8) + readData[1]) - 45  # 温度情報
    humi = 0.00152 * ((readData[3] << 8) + readData[4])      # 湿度情報

    return temp, humi


def main():
    """温度と湿度データをサーバーとして提供するメイン関数"""
    HOST = '0.0.0.0'  # すべてのインターフェースでリッスン
    PORT = 5000        # ポート番号 (適宜変更)

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.settimeout(60)  # 例: 60秒のタイムアウト
    server_socket.bind((HOST, PORT))
    server_socket.listen(3)  # 最大接続数: 1

    print(f"温度と湿度サーバーがポート {PORT} で起動しました。")

    try:
        while True:
            conn, addr = server_socket.accept()
            with conn:
                print(f"{addr} が接続しました。")
                while True:
                    try:
                        temperature, humidity = read_sht31()
                        data = f"{temperature:.1f},{humidity:.1f}"  # データフォーマット
                        conn.sendall(data.encode('utf-8'))  # データを送信
                        time.sleep(5)  # 5秒ごとに更新
                    except BrokenPipeError:
                        print("クライアントとの接続が切断されました。")
                        break # 内側のループを抜ける
                    except Exception as e:
                        print(f"送信エラーが発生しました: {e}")
                        break # 内側のループを抜ける

    except KeyboardInterrupt:
        print("サーバーを終了します。")
        server_socket.close()


if __name__ == "__main__":
    main()

AIすごい。

もう、ネットで探しまくることもない。

今は、常時起動して運用しています。

以上

次は、駅の電光掲示板みたいのを買って遊びたいと思います。

ラズパイの電源からは足りないと聞いていますので考え中