インターネット接続が切れ切れに

在宅ワークをしばらくすることになり、仕事部屋の、私用と仕事用のPC周り、ネットワークなどを少し変更しました。

模様替えもし、ルーターからPCの距離が少し遠くなったので、PCの近くに(スイッチング)ハブを置き、ルーターからは長いLANケーブルというレイアウトに。

しばらくは(気づかなかったのでしょうけど)問題なかったのですが、どうもメインで使用している私用PCのネットワークがよく切れる。
ネットサーフィン(死語)の途中でも切れ切れ。

タスクマネージャを確認すると、「パフォーマンス」に表示される「イーサネット」が時々消えます。

LANケーブルを抜いた時と同じ。

 

しばらくは原因がわからなかったのですが、どうもハブが原因のようです。

 

メインの私用PCは自作機で、昨年に組み上げたもの。

イーサネットはギガビットのものですが、ハブは一番安かった「

100BASE-TX対応スイッチングハブ

」でした。

混在していても問題なく、単に、遅いほうに合わせて本来の(ギガビットの)スピードが出ないだけ、と思っていたのですが、この商品はそうではないようです。

ルーターに直接差すと問題なく動作。

 

ギガ対応のハブを購入するかはわかりませんが、家の中にある、もう1台のハブと入れ替えてみます。

 

Raspberry Pi Python GUI アプリケーション 10:LCDに現在の日時、温度・湿度、CPU・メモリ情報・ネットワーク情報を表示

なんか、いまいちマルチスレッド化した各種情報表示処理がうまく終了してくれません。

一応改良版です。

ついでに、「ホスト名」「IPアドレス(V4)(インターフェース毎)」を表示するように修正しました。

手持ちの小さなLCDに表示するため、2行から1行に修正したりしています。

次は、何を表示しようか。

 

Pythonプログラミング

# -*- coding: utf-8 -*-
# 日本語コメントをエラーとしないように、UTF-8で保存

# 使用するライブラリのインポート
import datetime
import threading
import tkinter as Tk
from time import sleep
import psutil
import smbus
import socket

class Frame(Tk.Frame):
    """
    フレームを拡張する
    """
    def __init__(self, master = None):
        # ウィンドウを作成
        Tk.Frame.__init__(self, master)

    def setSize(self, width, height, isCenter = True):
        """
        サイズを指定、ディスプレイ中央に表示する
        """
        # サイズを指定した文字列
        geometry = "{0}x{1}".format(width, height)

        if isCenter:    # ディスプレイ中央に表示する            
            # ディスプレイの幅と高さを取得
            s_width = self.winfo_screenwidth()
            s_height = self.winfo_screenheight()
            # 表示する左上の座標を算出        
            win_left = int((s_width - width) / 2)
            win_top = int((s_height - height) / 2)
            # 表示位置を指定した文字列を追加
            geometry += "+{0}+{1}".format(win_left, win_top)

        # サイズと表示位置を中央に指定(幅 x 高さ + 左位置 + 上位置)
        self.master.geometry(geometry)

    def setFullScreen(self, isFullScreen = True):
        """
        ウィンドウのフルスクリーンを設定
        """
        self.master.fullScreenState = isFullScreen
        self.master.attributes("-fullscreen", self.master.fullScreenState)

    def setTitle(self, title):
        """
        タイトルを指定する
        """
        self.master.title(title)
    
    def disableMaximum(self):
        """
        最大化を無効化
        """
        self.master.resizable(False, False)

class label(Tk.Label):
    """
    labelクラスを拡張する
    """
    def setFormat(self, value):
        """
        フォーマット書式を指定する
        """
        self.__dict__['format'] = value
    
    def disp_formated(self, *args):
        """
        指定されているフォーマット書式で表示する
        """
        self['text'] = str(self.__dict__['format']).format(*args)

class guiParts():
    """
    GUI部品を追加表示する
    すべてグリッド、左寄せで表示
    """
    def addLabel(self, row, column, text = '', format = '', isLeft = True):
        """
        ラベルを追加表示する
        """
        # 初期表示テキストが設定されておらず、フォーマットが設定されている場合は、フォーマット文字列を表示
        if (text == '' and format != ''):
            lbl = label(self, text = format)
        else:
            lbl = label(self, text = text)
        
        # 表示フォント設定
        lbl['font'] = ("", 20)

        # 表示フォーマットを設定        
        lbl.setFormat(format)

        if isLeft:
            lbl.grid(row = row, column = column, pady = 5, padx = 20, sticky = Tk.W)
        else:
            lbl.grid(row = row, column = column, pady = 5, padx = 20, sticky = Tk.E)
        
        return lbl

    def addButton(self, row, column, text, width = None):
        """
        ボタンを追加表示する
        """
        btn = Tk.Button(self, text = text, width = width)
        btn.grid(row = row, column = column, sticky = Tk.W)
        # 表示フォント設定
        btn['font'] = ("", 15)

        return btn

class raspiinfo():
    @staticmethod
    def tempelature_humidity():
        """
        温度と湿度を取得する
        """
        ADR = 0x44
        # I2Cの取得
        ic2 = smbus.SMBus(1)
        # 測定頻度の設定
        ic2.write_byte_data(ADR, 0x23, 0x34)
        sleep(0.5)
        # 測定値の読出し
        ic2.write_byte_data(ADR, 0xe0, 0x0)
        sleep(0.5)
        # 6バイト取得
        d = ic2.read_i2c_block_data(ADR, 0x0, 6)

        # 0~1バイトが温度
        # 測定データ(生データ)を取得
        t = ((d[0]) << 8) | (d[1])
        # 換算する
        t = -45 + (175 * t / 65535)

        # 3~4バイトが湿度
        # 測定データ(生データ)を取得
        h = ((d[3]) << 8) | (d[4])
        # 換算する
        h = 100 * h / 65535

        # 結果を返却
        return t, h

    @staticmethod
    def cpu_temp():
        """
        CPU温度の取得
        """
        f = open("/sys/class/thermal/thermal_zone0/temp","r")
        cpu_temp = 0.0
        for t in f:
            cpu_temp = t[:2]+"."+t[2:]
        f.close()
        return float(cpu_temp)

    @staticmethod
    def cpu_percent():
        """
        CPU使用率の取得
        """
        cpu_percent = psutil.cpu_percent(interval=1)
        return round(cpu_percent)

    @staticmethod
    def memory_info():
        """
        メモリ情報(使用量[MB]、総量[MB])の取得
        """
        mem = psutil.virtual_memory()
        mem_used = round(mem.used / 1024 / 1024)
        mem_total = round(mem.total / 1024 / 1024)
        return (mem_used, mem_total)

    @staticmethod
    def IP_info():
        """
        ホスト名、IPアドレスの取得(IPアドレスはIF:IP(V4)アドレスkジェイ式で複数)
        """
        host = socket.gethostname()
        ip = []
        ipinfo = psutil.net_if_addrs()
        for _if in ipinfo.keys():                           # if毎の属性値
            for ifattr in str(ipinfo[_if]).split(','):      # ifの属性値
                if ifattr.strip().startswith('address='):
                    if ifattr.strip().split('=')[1] == "'127.0.0.1'":
                        # Localアドレスははじく
                        break
                    else:
                        ip.append('{0}:{1}'.format(_if,ifattr.strip().split("=")[1].strip("'")))
                        break

        return (host, ip)

class dispSub():
    @staticmethod
    def disp_clock():
        """
        現在時刻を表示
        """
        # 表示情報の取得
        now = datetime.datetime.now()
        # 表示
        lblClock.disp_formated(now)

    @staticmethod
    def disp_temp():
        """
        室温と湿度を表示
        """
        try:
            # 表示情報の取得
            templature , humidity = raspiinfo.tempelature_humidity()
            # 表示
            lblTemp.disp_formated(templature, humidity)
        except Exception as e:
            print(e)
            lblTemp.disp_formated(0, 0)

    @staticmethod
    def disp_cpu():
        """
        CPU温度とCPU使用率、メモリ情報を表示
        """    
        # 表示情報の取得
        cpu_info = (raspiinfo.cpu_temp(), raspiinfo.cpu_percent())
        mem_info = raspiinfo.memory_info()
        # 表示
        lblCpuTemp.disp_formated(cpu_info[0], cpu_info[1])
        lblMem.disp_formated(mem_info[0], mem_info[1], (mem_info[0] / mem_info[1]) * 100)

    @staticmethod
    def disp_if():
        """
        ホスト名とIPアドレスを表示
        """    
        # 表示情報の取得
        ip_info = raspiinfo.IP_info()
        # 表示
        lblHost.disp_formated(ip_info[0])
        lblIP.disp_formated(ip_info[1])

    @staticmethod
    def execute_disp_clock():
        """
        別スレッドで現在時刻表示処理実行
        """
        while True:
            # 現在時刻を表示する
            dispSub.disp_clock()

            # 500mS待ち受ける
            global runThread
            for t in range(0, 5):
                if runThread:
                    # 待つ(100mS)
                    sleep(0.1)
                else:
                    return

    @staticmethod
    def execute_disp_temp():
        """
        別スレッドで室温と温度を表示処理実行
        """
        while True:
            # 現在室温、湿度を表示する
            dispSub.disp_temp()

            # 2000mS待ち受ける
            global runThread
            for t in range(0, 20):
                if runThread:
                    # 待つ(100mS)
                    sleep(0.1)
                else:
                    return

    @staticmethod
    def execute_disp_cpu():
        """
        別スレッドでCPU情報とメモリ情報を表示処理実行
        """
        while True:
            # 現在室温、湿度を表示する
            dispSub.disp_cpu()

            # 2000mS待ち受ける
            global runThread
            for t in range(0, 20):
                if runThread:
                    # 待つ(100mS)
                    sleep(0.1)
                else:
                    return

    @staticmethod
    def execute_disp_IF():
        """
        別スレッドでホスト名とIPアドレスを表示処理実行
        """
        while True:
            # 現在室温、湿度を表示する
            dispSub.disp_if()

            # 10S待ち受ける
            global runThread
            for t in range(0, 100):
                if runThread:
                    # 待つ(100mS)
                    sleep(0.1)
                else:
                    return

def clickQuit(event):
    """
    終了する
    """
    # 別スレッド終了フラグをOFFに
    global runThread
    runThread = False
    # 別スレッドを終了させる
    global threads
    for thread in threads:
        thread._stop()
    # ウィンドウを閉じる
    global w
    w.master.destroy()
    # アプリケーション終了
    print('finished !!')

if __name__ == "__main__":
    global w
    # メインウィンドウを生成、各設定を行う
    w = Frame()
    w.setTitle('Raspberry Pi 情報')
    # サイズは指定せず、フルスクリーンで表示する
    w.setFullScreen(True)     
    w.pack()

    # 項目ラベル
    guiParts.addLabel(w, 0, 0, '日時 時刻:'        , isLeft = False)
    guiParts.addLabel(w, 1, 0, '室温 / 湿度:'      , isLeft = False)
    guiParts.addLabel(w, 2, 0, 'CPU温度 / 使用率:' , isLeft = False)
    guiParts.addLabel(w, 3, 0, 'メモリ情報:'       , isLeft = False)
    guiParts.addLabel(w, 4, 0, 'host:'            , isLeft = False)
    guiParts.addLabel(w, 5, 0, 'IP(V4):'          , isLeft = False)
    # 項目値
    lblClock    = guiParts.addLabel(w, 0, 1, format = '{0:%Y/%m/%d(%a) %H:%M:%S}')
    lblTemp     = guiParts.addLabel(w, 1, 1, format = '{0:>5.1f} [°C] / {1:>5.1f} [%]')
    lblCpuTemp  = guiParts.addLabel(w, 2, 1, format = '{0:>5.1f} [°C] / {1:>5.1f} [%]')
    lblMem      = guiParts.addLabel(w, 3, 1, format = '{0:>4} / {1:>4} [MB] ({2:>5.1f} [%])')
    lblHost     = guiParts.addLabel(w, 4, 1, format = '{0}')
    lblIP       = guiParts.addLabel(w, 5, 1, format = '{0}')
    btnQuit     = guiParts.addButton(w, 6, 1, '終了', 10)

    # 別スレッドでの実行フラグをONに
    global runThread
    runThread = True
    # 別スレッドで再表示処理を実行
    global threads
    threads = []
    threads.append(threading.Thread(target=dispSub.execute_disp_clock))
    threads.append(threading.Thread(target=dispSub.execute_disp_temp))
    threads.append(threading.Thread(target=dispSub.execute_disp_cpu))
    threads.append(threading.Thread(target=dispSub.execute_disp_IF))
    for thread in threads:
        thread.start()

    # 終了ボタンに終了処理をバインド
    btnQuit.bind('<1>', clickQuit)

    # メインウィンドウをメインスレッドとして実行
    w.mainloop()

 

Raspberry Pi Python GUI アプリケーション 09:LCDに現在の日時、温度・湿度、CPU・メモリ情報を表示(フォント大きく)

前回の内容と変わらないのですが、接続したLCDに表示した内容が、フォントが小さく見づらいです。

ですので、今回はフォントを大きくします。

また、ウィンドウはフルスクリーンとしておきます。

フルスクリーン表示としたことで、ウィンドウを閉じるボタンが非表示となってしまったので、終了ボタンを追加、終了処理をバインドしています。

サブスレッドが終了するのを待ち合わせるようにしているので、おそらくエラーなく終了してくれると思います。

 

Pythonプログラミング

# -*- coding: utf-8 -*-
# 日本語コメントをエラーとしないように、UTF-8で保存

# 使用するライブラリのインポート
import datetime
import threading
import tkinter as Tk
from time import sleep
import psutil
import smbus

class Frame(Tk.Frame):
    """
    フレームを拡張する
    """
    def __init__(self, master = None):
        # ウィンドウを作成
        Tk.Frame.__init__(self, master)

    def setSize(self, width, height, isCenter = True):
        """
        サイズを指定、ディスプレイ中央に表示する
        """
        # サイズを指定した文字列
        geometry = "{0}x{1}".format(width, height)

        if isCenter:    # ディスプレイ中央に表示する            
            # ディスプレイの幅と高さを取得
            s_width = self.winfo_screenwidth()
            s_height = self.winfo_screenheight()
            # 表示する左上の座標を算出        
            win_left = int((s_width - width) / 2)
            win_top = int((s_height - height) / 2)
            # 表示位置を指定した文字列を追加
            geometry += "+{0}+{1}".format(win_left, win_top)

        # サイズと表示位置を中央に指定(幅 x 高さ + 左位置 + 上位置)
        self.master.geometry(geometry)

    def setFullScreen(self, isFullScreen = True):
        """
        ウィンドウのフルスクリーンを設定
        """
        self.master.fullScreenState = isFullScreen
        self.master.attributes("-fullscreen", self.master.fullScreenState)

    def setTitle(self, title):
        """
        タイトルを指定する
        """
        self.master.title(title)
    
    def disableMaximum(self):
        """
        最大化を無効化
        """
        self.master.resizable(False, False)

class label(Tk.Label):
    """
    labelクラスを拡張する
    """
    def setFormat(self, value):
        """
        フォーマット書式を指定する
        """
        self.__dict__['format'] = value
    
    def disp_formated(self, *args):
        """
        指定されているフォーマット書式で表示する
        """
        self['text'] = str(self.__dict__['format']).format(*args)

class guiParts():
    """
    GUI部品を追加表示する
    すべてグリッド、左寄せで表示
    """
    def addLabel(self, row, column, text = '', format = '', isLeft = True):
        """
        ラベルを追加表示する
        """
        # 初期表示テキストが設定されておらず、フォーマットが設定されている場合は、フォーマット文字列を表示
        if (text == '' and format != ''):
            lbl = label(self, text = format)
        else:
            lbl = label(self, text = text)
        
        # 表示フォント設定
        lbl['font'] = ("", 20)

        # 表示フォーマットを設定        
        lbl.setFormat(format)

        if isLeft:
            lbl.grid(row = row, column = column, pady = 5, padx = 20, sticky = Tk.W)
        else:
            lbl.grid(row = row, column = column, pady = 5, padx = 20, sticky = Tk.E)
        
        return lbl

    def addButton(self, row, column, text, width = None):
        """
        ボタンを追加表示する
        """
        btn = Tk.Button(self, text = text, width = width)
        btn.grid(row = row, column = column, sticky = Tk.W)
        # 表示フォント設定
        btn['font'] = ("", 15)

        return btn

class raspiinfo():
    @staticmethod
    def tempelature_humidity():
        """
        温度と湿度を取得する
        """
        ADR = 0x44
        # I2Cの取得
        ic2 = smbus.SMBus(1)
        # 測定頻度の設定
        ic2.write_byte_data(ADR, 0x23, 0x34)
        sleep(0.5)
        # 測定値の読出し
        ic2.write_byte_data(ADR, 0xe0, 0x0)
        sleep(0.5)
        # 6バイト取得
        d = ic2.read_i2c_block_data(ADR, 0x0, 6)

        # 0~1バイトが温度
        # 測定データ(生データ)を取得
        t = ((d[0]) << 8) | (d[1])
        # 換算する
        t = -45 + (175 * t / 65535)

        # 3~4バイトが湿度
        # 測定データ(生データ)を取得
        h = ((d[3]) << 8) | (d[4])
        # 換算する
        h = 100 * h / 65535

        # 結果を返却
        return t, h

    @staticmethod
    def cpu_temp():
        """
        CPU温度の取得
        """
        f = open("/sys/class/thermal/thermal_zone0/temp","r")
        cpu_temp = 0.0
        for t in f:
            cpu_temp = t[:2]+"."+t[2:]
        f.close()
        return float(cpu_temp)

    @staticmethod
    def cpu_percent():
        """
        CPU使用率の取得
        """
        cpu_percent = psutil.cpu_percent(interval=1)
        return round(cpu_percent)

    @staticmethod
    def memory_info():
        """
        メモリ情報(使用量[MB]、総量[MB])の取得
        """
        mem = psutil.virtual_memory()
        mem_used = round(mem.used / 1024 / 1024)
        mem_total = round(mem.total / 1024 / 1024)
        return (mem_used, mem_total)

class dispSub():
    @staticmethod
    def disp_clock():
        """
        現在時刻を表示
        """
        # 表示情報の取得
        now = datetime.datetime.now()
        # 表示
        lblClock.disp_formated(now)

    @staticmethod
    def disp_temp():
        """
        室温と湿度を表示
        """
        try:
            # 表示情報の取得
            templature , humidity = raspiinfo.tempelature_humidity()
            # 表示
            lblTemp.disp_formated(templature)
            lblHum.disp_formated(humidity)
        except Exception as e:
            print(e)
            lblTemp.disp_formated(0)
            lblHum.disp_formated(0)

    @staticmethod
    def disp_cpu():
        """
        CPU温度とCPU使用率、メモリ情報を表示
        """    
        # 表示情報の取得
        cpu_info = (raspiinfo.cpu_temp(), raspiinfo.cpu_percent())
        mem_info = raspiinfo.memory_info()
        # 表示
        lblCpuTemp.disp_formated(cpu_info[0])
        lblCpuPer.disp_formated(cpu_info[1])
        lblMem.disp_formated(mem_info[0], mem_info[1], (mem_info[0] / mem_info[1]) * 100)

    @staticmethod
    def execute_disp_clock():
        """
        別スレッドで現在時刻表示処理実行
        """
        while runThread:
            # 現在時刻を表示する
            dispSub.disp_clock()
            # 待つ(500mS)
            sleep(0.5)

    @staticmethod
    def execute_disp_temp():
        """
        別スレッドで室温と温度を表示処理実行
        """
        while runThread:
            # 現在室温、湿度を表示する
            dispSub.disp_temp()
            # 待つ(2000mS)
            sleep(2)

    @staticmethod
    def execute_disp_cpu():
        """
        別スレッドでCPU情報とメモリ情報を表示処理実行
        """
        while runThread:
            # 現在室温、湿度を表示する
            dispSub.disp_cpu()
            # 待つ(2000mS)
            sleep(2)

def clickQuit(event):
    """
    終了する
    """
    # 別スレッド終了フラグをOFFに
    global runThread
    runThread = False
    # 別スレッドが終了するのを待ち合わせる
    global threads    
    for thread in threads:
        thread.join()
    # ウィンドウを閉じる
    global w
    w.master.destroy()
    # アプリケーション終了
    print('finished !!')

if __name__ == "__main__":
    global w
    # メインウィンドウを生成、各設定を行う
    w = Frame()
    w.setTitle('Raspberry Pi 情報')
    # サイズは指定せず、フルスクリーンで表示する
    w.setFullScreen(True)     
    w.pack()

    # 項目ラベル
    guiParts.addLabel(w, 0, 0, '現在の日時 時刻:', isLeft = False)
    guiParts.addLabel(w, 1, 0, '室温:'          , isLeft = False)
    guiParts.addLabel(w, 2, 0, '湿度:'          , isLeft = False)
    guiParts.addLabel(w, 3, 0, 'CPU温度:'       , isLeft = False)
    guiParts.addLabel(w, 4, 0, 'CPU使用率:'     , isLeft = False)
    guiParts.addLabel(w, 5, 0, 'メモリ情報:'    , isLeft = False)
    # 項目値
    lblClock    = guiParts.addLabel(w, 0, 1, format = '{0:%Y/%m/%d(%a) %H:%M:%S}')
    lblTemp     = guiParts.addLabel(w, 1, 1, format = '{0:>5.1f} [°C]')
    lblHum      = guiParts.addLabel(w, 2, 1, format = '{0:>5.1f} [%]')
    lblCpuTemp  = guiParts.addLabel(w, 3, 1, format = '{0:>5.1f} [°C]')
    lblCpuPer   = guiParts.addLabel(w, 4, 1, format = '{0:>5.1f} [%]')
    lblMem      = guiParts.addLabel(w, 5, 1, format = '{0:>4} / {1:>4} [MB] ({2:>5.1f} [%])')
    btnQuit     = guiParts.addButton(w, 6, 1, '終了', 10)

    # 別スレッドでの実行フラグをONに
    global runThread
    runThread = True
    # 別スレッドで再表示処理を実行
    global threads
    threads = []
    threads.append(threading.Thread(target=dispSub.execute_disp_clock))
    threads.append(threading.Thread(target=dispSub.execute_disp_temp))
    threads.append(threading.Thread(target=dispSub.execute_disp_cpu))
    for thread in threads:
        thread.start()

    # 終了ボタンに終了処理をバインド
    btnQuit.bind('<1>', clickQuit)

    # メインウィンドウをメインスレッドとして実行
    w.mainloop()

Raspberry Pi Python GUI アプリケーション 08:LCDに現在の日時、温度・湿度、CPU・メモリ情報を表示

入手したLCDディスプレイですが、サイズは非常に小さいながらも、2行しか表示できなかったディスプレイに比べ、表現力が随分上がったので楽しいです。

Amazonで2つ購入したのですが、1つは正常動作、もう一つは画面表示はできるのですが、タッチ操作ができません。

現在、問い合わせ中。

お隣の大陸まで送り返せと言われても…返品不要で別なもの送ってくれないかなぁ。

 

文字の装飾は全くしていませんが、今回はこんな仕様。

・ウィンドウを1つ表示して、以下の情報を定期的に書き換える

 ・現在の日時、時刻を0.5秒間隔で

 ・I2C接続した温度・湿度センサーの情報を2秒間隔で

 ・CPU温度、使用率とメモリ使用量、総量と使用率を2秒間隔で

 

 

1. 配線図

  LCDディスプレイは、Pin番号の1~28に差し込みます。

  実は、すべてのピンを使用しているわけではなく、SDA、SCLあたりは使っていません。

  このディスプレイはI2C接続ではないため、「刺さってはいるが、配線していない」状態です。

  LCDディスプレイを差し込むと、1~28のPinは使用できなくなるので、分岐ケーブルなどを使用する、もしくはデータシートを参照して、必要なPinのみをLCDディスプレイに差し込む、などの手段があります。

  配線図、わかりづらくてすみません。

f:id:kobatom5278:20210225221757p:plain

2. Pythonプログラミング

  メインスレッドは画面表示自体です。

  ほかに、3つスレッドを作成して、それぞれでウィンドウのラベルを書き換えています。

  それほど面倒なことはしていないのですが、ラベルクラスにフォーマットプロパティを追加、そのフォーマットで値を表示してあげるような処理を組んでいます。

  表示したい各情報の取得は以前の記事のものをちょっと変更したくらいで、基本は変わりません。

 

# -*- coding: utf-8 -*-
# 日本語コメントをエラーとしないように、UTF-8で保存

# 使用するライブラリのインポート
import datetime
import threading
import tkinter as Tk
from time import sleep
import psutil
import smbus

class Frame(Tk.Frame):
    """
    フレームを拡張する
    """
    def __init__(self, master = None):
        # ウィンドウを作成
        Tk.Frame.__init__(self, master)

    def setSize(self, width, height, isCenter = True):
        """
        サイズを指定、ディスプレイ中央に表示する
        """
        # サイズを指定した文字列
        geometry = "{0}x{1}".format(width, height)

        if isCenter:    # ディスプレイ中央に表示する            
            # ディスプレイの幅と高さを取得
            s_width = self.winfo_screenwidth()
            s_height = self.winfo_screenheight()
            # 表示する左上の座標を算出        
            win_left = int((s_width - width) / 2)
            win_top = int((s_height - height) / 2)
            # 表示位置を指定した文字列を追加
            geometry += "+{0}+{1}".format(win_left, win_top)

        # サイズと表示位置を中央に指定(幅 x 高さ + 左位置 + 上位置)
        self.master.geometry(geometry)
    
    def setTitle(self, title):
        """
        タイトルを指定する
        """
        self.master.title(title)
    
    def disableMaximum(self):
        """
        最大化を無効化
        """
        self.master.resizable(False, False)

class label(Tk.Label):
    """
    labelクラスを拡張する
    """
    def setFormat(self, value):
        """
        フォーマット書式を指定する
        """
        self.__dict__['format'] = value
    
    def disp_formated(self, *args):
        """
        指定されているフォーマット書式で表示する
        """
        self['text'] = str(self.__dict__['format']).format(*args)

class guiParts():
    """
    GUI部品を追加表示する
    すべてグリッド、左寄せで表示
    """
    def addLabel(self, row, column, text = '', format = ''):
        """
        ラベルを追加表示する
        """
        # 初期表示テキストが設定されておらず、フォーマットが設定されている場合は、フォーマット文字列を表示
        if (text == '' and format != ''):
            lbl = label(self, text = format)
        else:
            lbl = label(self, text = text)

        # 表示フォーマットを設定        
        lbl.setFormat(format)

        lbl.grid(row = row, column = column, pady = 5, padx = 20, sticky = Tk.W)
        
        return lbl

class raspiinfo():
    @staticmethod
    def tempelature_humidity():
        """
        温度と湿度を取得する
        """
        ADR = 0x44
        # I2Cの取得
        ic2 = smbus.SMBus(1)
        # 測定頻度の設定
        ic2.write_byte_data(ADR, 0x23, 0x34)
        sleep(0.5)
        # 測定値の読出し
        ic2.write_byte_data(ADR, 0xe0, 0x0)
        sleep(0.5)
        # 6バイト取得
        d = ic2.read_i2c_block_data(ADR, 0x0, 6)

        # 0~1バイトが温度
        # 測定データ(生データ)を取得
        t = ((d[0]) << 8) | (d[1])
        # 換算する
        t = -45 + (175 * t / 65535)

        # 3~4バイトが湿度
        # 測定データ(生データ)を取得
        h = ((d[3]) << 8) | (d[4])
        # 換算する
        h = 100 * h / 65535

        # 結果を返却
        return t, h

    @staticmethod
    def cpu_temp():
        """
        CPU温度の取得
        """
        f = open("/sys/class/thermal/thermal_zone0/temp","r")
        cpu_temp = 0.0
        for t in f:
            cpu_temp = t[:2]+"."+t[2:]
        f.close()
        return float(cpu_temp)

    @staticmethod
    def cpu_percent():
        """
        CPU使用率の取得
        """
        cpu_percent = psutil.cpu_percent(interval=1)
        return round(cpu_percent)

    @staticmethod
    def memory_info():
        """
        メモリ情報(使用量[MB]、総量[MB])の取得
        """
        mem = psutil.virtual_memory()
        mem_used = round(mem.used / 1024 / 1024)
        mem_total = round(mem.total / 1024 / 1024)
        return (mem_used, mem_total)

class dispSub():
    @staticmethod
    def disp_clock():
        """
        現在時刻を表示
        """
        # 表示情報の取得
        now = datetime.datetime.now()
        # 表示
        lblClock.disp_formated(now)

    @staticmethod
    def disp_temp():
        """
        室温と湿度を表示
        """
        try:
            # 表示情報の取得
            templature , humidity = raspiinfo.tempelature_humidity()
            # 表示
            lblTemp.disp_formated(templature)
            lblHum.disp_formated(humidity)
        except Exception as e:
            print(e)
            lblTemp.disp_formated(0)
            lblHum.disp_formated(0)

    @staticmethod
    def disp_cpu():
        """
        CPU温度とCPU使用率、メモリ情報を表示
        """    
        # 表示情報の取得
        cpu_info = (raspiinfo.cpu_temp(), raspiinfo.cpu_percent())
        mem_info = raspiinfo.memory_info()
        # 表示
        lblCpuTemp.disp_formated(cpu_info[0])
        lblCpuPer.disp_formated(cpu_info[1])
        lblMem.disp_formated(mem_info[0], mem_info[1], (mem_info[0] / mem_info[1]) * 100)

    @staticmethod
    def execute_disp_clock():
        """
        別スレッドで現在時刻表示処理実行
        """
        while runThread:
            # 現在時刻を表示する
            dispSub.disp_clock()
            # 待つ(500mS)
            sleep(0.5)

    @staticmethod
    def execute_disp_temp():
        """
        別スレッドで室温と温度を表示処理実行
        """
        while runThread:
            # 現在室温、湿度を表示する
            dispSub.disp_temp()
            # 待つ(2000mS)
            sleep(2)

    @staticmethod
    def execute_disp_cpu():
        """
        別スレッドでCPU情報とメモリ情報を表示処理実行
        """
        while runThread:
            # 現在室温、湿度を表示する
            dispSub.disp_cpu()
            # 待つ(2000mS)
            sleep(2)

if __name__ == "__main__":
    global w
    # メインウィンドウを生成、各設定を行う
    w = Frame()
    w.setTitle('Raspberry Pi 情報')
    w.setSize(500, 220)
    w.pack()

    # 項目ラベル
    guiParts.addLabel(w, 0, 0, '現在の日時 時刻:')
    guiParts.addLabel(w, 1, 0, '室温:')
    guiParts.addLabel(w, 2, 0, '湿度:')
    guiParts.addLabel(w, 3, 0, 'CPU温度:')
    guiParts.addLabel(w, 4, 0, 'CPU使用率:')
    guiParts.addLabel(w, 5, 0, 'メモリ情報:')
    # 項目値
    lblClock    = guiParts.addLabel(w, 0, 1, format = '{0:%Y/%m/%d(%a) %H:%M:%S}')
    lblTemp     = guiParts.addLabel(w, 1, 1, format = '{0:>5.1f} [°C]')
    lblHum      = guiParts.addLabel(w, 2, 1, format = '{0:>5.1f} [%]')
    lblCpuTemp  = guiParts.addLabel(w, 3, 1, format = '{0:>5.1f} [°C]')
    lblCpuPer   = guiParts.addLabel(w, 4, 1, format = '{0:>5.1f} [%]')
    lblMem      = guiParts.addLabel(w, 5, 1, format = '{0:>4} / {1:>4} [MB] ({2:>5.1f} [%])')

    # 別スレッドでの実行フラグをONに
    global runThread
    runThread = True
    # 別スレッドで再表示処理を実行
    threads = []
    threads.append(threading.Thread(target=dispSub.execute_disp_clock))
    threads.append(threading.Thread(target=dispSub.execute_disp_temp))
    threads.append(threading.Thread(target=dispSub.execute_disp_cpu))
    for thread in threads:
        thread.start()

    # メインウィンドウをメインスレッドとして実行
    w.mainloop()

    # メインウィンドウが閉じられたら別スレッド終了フラグをOFFに
    runThread = False
    # 別スレッドが終了するのを待ち合わせる
    for thread in threads:
        thread.join()
    # アプリケーション終了
    print('finished !!')

 

Raspberry Pi Python GUI アプリケーション 07:現在の日時と

小さな(3.5インチ)のディスプレイを入手したので、少し、GUIアプリケーションを作ろうかと思います。
2行しか表示できなかったLCDディスプレイに比べ、小さいながらも多くの情報を出力でき、しかもカラー。

表現力は段違いに広がると思います。

 

今回は、「定期的に表示内容を書き換える」ことが目的なので、見栄えは気にしません。

文字の大きさ、フォント、色などについては気にしません。

現在の日付、時刻を表示するのみです。

一応、書き換え処理の実施有無がわかるように、最後に"."を点滅させてみます。

 

Pythonプログラミング

  以前作成したものを、使用していない関数を削除、定期的に書き換えるために「after」メソッドを使用しています。

  多くのOSがそうですが、GUIアプリケーションはメインスレッドで動きます。

  定期的な処理(書き換え)を行うためには、別スレッドで行ったりするのですが、「after」メソッドを使えば、実施間隔をmS単位で指定、「勝手に」実行してくれるイメージです。

# -*- coding: utf-8 -*-
# 日本語コメントをエラーとしないように、UTF-8で保存

# 使用するライブラリのインポート
import tkinter as Tk
import datetime

# 表示するウィンドウの幅と高さ
WINDOW_WIDTH = 500
WINDOW_HEIGHT = 220

class Frame(Tk.Frame):
    """
    フレームを生成する
    """
    def __init__(self, master = None):
        # ウィンドウを作成
        Tk.Frame.__init__(self, master)

    def setSize(self, width, height, isCenter = True):
        """
        サイズを指定、ディスプレイ中央に表示する
        """
        # サイズを指定した文字列
        geometry = "{0}x{1}".format(width, height)

        if isCenter:    # ディスプレイ中央に表示する            
            # ディスプレイの幅と高さを取得
            s_width = self.winfo_screenwidth()
            s_height = self.winfo_screenheight()
            # 表示する左上の座標を算出        
            win_left = int((s_width - width) / 2)
            win_top = int((s_height - height) / 2)
            # 表示位置を指定した文字列を追加
            geometry += "+{0}+{1}".format(win_left, win_top)

        # サイズと表示位置を中央に指定(幅 x 高さ + 左位置 + 上位置)
        self.master.geometry(geometry)
    
    def setTitle(self, title):
        """
        タイトルを指定する
        """
        self.master.title(title)
    
    def disableMaximum(self):
        """
        最大化を無効化
        """
        self.master.resizable(False, False)

class guiParts():
    """
    GUI部品を追加表示する
    すべてグリッド、左寄せで表示
    """

    def addLabel(self, row, column, text):
        """
        ラベルを追加表示する
        """
        lbl = Tk.Label(self, text = text)
        lbl.grid(row = row, column = column, pady = 5, padx = 20, sticky = Tk.W)
        
        return lbl

def disp_clock():
    """
    定期的に実行、現在時刻をラベルに表示
    """
    # 現在時刻の取得
    now = datetime.datetime.now()
    global isDot
    dot = '.' if isDot == True else ' '
    # 日時の書き換え
    lblClock['text'] = f'{now.strftime("%Y/%m/%d %H:%M:%S")}{dot}'

    isDot = not isDot
    # 500mS後に再帰呼出し
    w.after(500, disp_clock)

# 継承した場合など、実行されないようにする(現状、意味は特にない)
if __name__ == "__main__":
    global w
    # メインウィンドウを生成、各設定を行う
    w = Frame()
    w.setTitle('現在の日時と時刻')
    w.setSize(WINDOW_WIDTH, WINDOW_HEIGHT)
    w.disableMaximum()

    w.pack()

    # 現在日時
    guiParts.addLabel(w, 0, 0, '現在の日時 時刻:')
    lblClock =  guiParts.addLabel(w, 0, 1, 'yyyy:mm:dd hh:MM:ss')

    # 現在時刻を表示する
    isDot = False
    disp_clock()
    
    w.mainloop()

 

Raspberry Pi 格安LCDディスプレイ(タッチパッド機能付き)の接続

2行のディスプレイでは限界があり、大きなPCディスプレイも邪魔なので、GPIO接続の、格安(2000円未満)のディスプレイを購入してみました。さらにはタッチパッドでマウスもいらず。

 

 

1. 物品購入(GPIOの分岐)

  多くの類似品が販売されていますが、GPIO接続の、3.5インチのLCDディスプレイであれば、ほとんどが互換品だと思います。

  なお、GPIO接続するので、GPIOの分岐も、必要な方は購入したほうが良いです。

 

  ・GPIOの分岐(ディスプレイのみ接続するなら不要です)

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

プロトタイピングボード Raspberry Pi用
価格:736円(税込、送料別) (2021/2/23時点)

楽天で購入

 


 


2. 物品購入(LCDディスプレイ) 

  LCDディスプレイです。

  いろいろなサイズ、微妙な基盤が違う商品があるようですが、おそらく、3.5インチのGPIO接続、のものは、みんな一緒と思います。(未確認です。すみません)


 


 

 

  おそらく、これと同じものです。

www.waveshare.com

 

3. Rapsberry Pi との接続

  詳細は省きますが、GPIOピンの5Vの端子のある側に指すのみです。

  番号でいうところの、1番ピン。

  電源を切った状態で接続しましょう。

 

4. ドライバのインストール

  GPIOで接続、電源を入れると、うまく接続できていればバックライトが光ります。

  ただ、何も映らず、真っ白と思います。

  ここで、必要なドライバをインストールしていきます。

  すべて、LXTerminalにて行います。

  3行目の「LCD35-show」ですが、接続しているディスプレイに合わせたものを実行します。

  2行目を実行したところで、「LCD-show」フォルダの中を「ls」コマンドで一覧表示、あたりを付けて実行してみます。
  実行すると、再起動されると思います。

git clone https://github.com/waveshare/LCD-show.git
cd LCD-show/
./LCD35-show

 

5. 画面の回転

  LCDディスプレイに表示される向きを回転できます。

  方法としては、「4.」で実行した3行目のコマンドに、角度を渡してあげるだけ。

  これは、上下逆、180度回転させた状態です。

./LCD35-show 180

 

6. 画面解像度の変更

  初期状態では、LCDディスプレイの解像度に合わせた「320 * 480」(縦 * 横)の、非常に小さな表示領域となっています。

  当然、字は小さくなりますが、解像度はある程度変更可能です。

  Raspberry Piの設定から行えるのですが、表示領域が狭いので、コマンドでの設定をおすすめします。

  以下のコマンドで設定画面が開いたら、「2. Display Options」→「D1 Resolution」と進みます。

  「1024 x 768」あたりが無難と思います。

sudo raspi-config

 

7. タッチの補正

  ディスプレイをタッチした際の、カーソル位置の補正です。

  画面解像度を変更した場合は必須です。

  まずは、アプリケーションのインストール。

sudo apt-get install xinput-calibrator

   インストール後、Raspberry Pi OS の「設定」メニューに「Calibrate Touchscreen」というアプリケーションが追加されています。

  これを起動、画面に表示された赤い記号(標的のようなもの)をタッチします。LCDディスプレイです。ペンでのタッチをおすすめします。

  4か所終わると、結果が表示されます。

  「Section "InputClass"」の行から「EndSection」までをクリップボードにコピーします。

  次に、以下のコマンドで設定ファイルを開き、先ほどの内容を貼り付けます。

  2回目以降は、1行目のコマンドは不要です。

sudo mkdir /etc/X11/xorg.conf.d
sudo nano /etc/X11/xorg.conf.d/99-calibration.conf

 

Raspberry Pi OS インストール後にすること

備忘録代わりです。

Raspberry Pi OSを新規インストール後に実施することのメモです。
(正直な話をすると、必要なライブラリをインストールしたか確かめようと、必要なライブラリもアンインストールしてしまったので、再セットアップしています)

 

 

1. OS を SDカードへインストール

  Windows PC で行います。

  Raspberry Pi Imager を使用すると非常に楽。

 

2. SDカードを挿入して起動

  起動します。

  初期状態ではリモート接続できないので、HDMIでディスプレイ、USBでマウスとキーボードを物理接続します。

  Raspberry Pi 4 の場合は、有線LAN(Ethernet)を接続することも忘れずに。

  なお、電源を入れる際は、すべて接続してから電源ONのほうがよさそうです。

 

3. 初期設定

  最低限の初期設定を、まずは行います。

  初回起動時に、半強制的に行わせられる内容です。

  内容は以前の記事をご参考にしていただければ、と。

  ただ、Raspberry Pi OS のバージョンアップにより、内容が変わるかもしれません。

letraspberry.hatenablog.com

 

4. リモートデスクトップの有効化

  自分の場合は、Windows PC メインで使用、Raspberry Pi は周辺機器的な使い方をしています。

  ですので、Rapsberry Pi はリモートで接続して使用しています。

  基本的な設定方法は、以前の記事をご参照ください。

  ついでに、「カメラ」「SSH」「I2C」あたりも有効にしておきます。

letraspberry.hatenablog.com

letraspberry.hatenablog.com

 

5. 日本語の有効化

  これも、以前の記事をご参照ください

letraspberry.hatenablog.com

 

6. ファンの設定

  Rapsberry Pi 4の場合は、ファンも有効に。

letraspberry.hatenablog.com

 

7.  VisualStudioCodeのインストール

  開発環境にしたいのなら、こちらを。

letraspberry.hatenablog.com letraspberry.hatenablog.com

 

8. Raspberry Pi に共有フォルダを作成する

  Windows PC からファイルを共有したいので、共有ファイルを作成します。

letraspberry.hatenablog.com

 

9. SSH接続設定

  Windows PC から、SSHRaspberry Pi へ接続できるようにします。

  WindowsPCで起動したVisualStudioCodeで、RapsberryPiへリモートデバッグできるようにすると、すごく便利です。

letraspberry.hatenablog.com

letraspberry.hatenablog.com

letraspberry.hatenablog.com

 

10. Swapの無効化

  Rapsberry Pi のメモリ搭載量にもよりますが、4GBモデルの場合は無効にしてしまったほうが、SDカードの寿命が延びるかと思います。

  これは、機種などにより任意で。

letraspberry.hatenablog.com