Raspberry Pi ドライブレコーダーの作成④ 起動のチューニング

ドライブレコーダーとして、できるだけエンジンONとともに録画を開始したいものです。

専用ハードでもなければ、今回はZeroを使用していることもあり、ちょっと限界はあるのですが、頑張ってみようと思います。

なお、計測した起動時間は使用するMicro SD カードや室温などにも左右されるかと思いますので目安としてください。

 

1.起動時間の測定方法

  以下のコマンドで、起動するまでの時間が確認できます。

  あくまで、各サービスを起動し終わるまでの時間のようですので、目安です。

$systemd-analyze

  なお、各サービスごとの起動時間は以下コマンドで確認できます。

$systemd-analyze blame

  現状での起動時間の測定結果(3回)は以下です。

  <37.733s 37.005s 36.677s>

 

2.クロックアップ

  自作PC系でありがちな、CPUなどの動作クロックを上げて基本性能を上げようというものです。

  Zeroは発熱があまりないようですが、ヒートシンクはつけてきたいですね。

  手順ですが、「/boot/config.txt」に以下を追記、再起動するだけです。

# SD カードの読み書きを高速化する
dtoverlay=sdtweak,overclock_50=100
# CPUクロックを高速に(常時)
arm_freq=1000 
sdram_freq=500
force_turbo=1
 
  この結果の起動時間の測定結果(3回)は以下です。意外と効果ありです。

  <30.591s 30.859s 30.953s>

 

3.不要サービスを起動しない①

  不要そうなサービスを順次起動しないようにしてみます。

  むやみに起動しないようにすると、最低限必要な機能も損なわれるので注意です。

  以下コマンドを実行します。(スワップの無効かと、日々の更新かと思います)

  あまり効果がないような気もしますが…

$sudo systemctl disable dphys-swapfile.service
$sudo systemctl disable apt-daily.service
  この結果の起動時間の測定結果(3回)は以下です。意外と効果ありです。

  <30.546s 30.930s 31.125s>

 

4.不要サービスを起動しない②

  今回の仕様ではBlueToothは使用しないので、バッサリと。

$sudo systemctl stop hciuart.service
$sudo systemctl stop bluealsa.service
$sudo systemctl stop bluetooth.service
$sudo systemctl disable hciuart.service
$sudo systemctl disable bluealsa.service
$sudo systemctl disable bluetooth.service
  この結果の起動時間の測定結果(3回)は以下です。思ったより…でした。

  <31.454s 30.159s 30.814s>

 

5.不要サービスを起動しない③

  申し越し頑張ってみます。システムログもいらないので止めてしまいます。

$sudo systemctl stop rsyslog.service
$sudo systemctl disable rsyslog.service
$sudo service rsyslog stop
  この結果の起動時間の測定結果(3回)は以下です。誤差かもしれませんが、30秒切りました。

  <30.589s 29.545s 30.415s>

 

6.不要サービスを起動しない④

$systemd-analyze blame

  で上位にいた、USBキーボード関連らしきものも止めてしまします。

  これにより、USBキーボードを接続しても操作できなくなると思われます。

$sudo systemctl disable keyboard-setup
$sudo systemctl disable kbd

  <29.3876s 30.600s 29.826s>

 

7.最後のとどめ

  これ以上は無理だろうな、ということで以下を実施してみました。

  起動時にデスクトップ(いわゆるGUI、XWindow)を起動せず、コンソール画面にログインした状態とする

  ※$sudo raspi-config で、設定できます。

    < 29.150s 29.541s 29.489s >

  スプラッシュ画面の非表示。

  「/boot/config.txt」に

disable_splash=1

  を追記します。

    < 28.363s 29.300s 28.580s >

  起動時表示の簡素化。

  「/boot/config.txt」に

boot_delay=0

  を追記します。

    < 28.758s 29.103s 28.295s >

   止めてよいかわかず迷った不要サービスの起動を止めた

$sudo systemctl disable raspi-config.service

    < 27.739s 28.354s 28.124s >

 

8.まとめ

  何もしないよりは10秒ほど短縮、30秒は切りました。

  我が家の場合は、

  車のカギを開けて → エンジンON → 子供たち乗せて、ベルトの確認 → 出発の準備完了

  ですので、まぁ、許容とします。
  

Raspberry Pi ドライブレコーダーの作成③ 初期セットアップ

ドライブレコーダーというより、Raspberry Pi Zeroのありきたりなセットアップです。

 

1.OSインストール

  いつもであれば最新OSをインストールするのですが、原因がよくわからないのですが、1つ前のバージョンを使用しました。

  手順としては、以下のURLからOSのイメージ(ZIP)ファイルをダウンロード、「Raspberry Pi Imager」を使用してMicro SD カードを作成です。

  ・OSイメージのダウンロード(ここにある最新日付が、1つ前のバージョンのようです)    

downloads.raspberrypi.org

 

2.OSセットアップ(RaspberryPi Zeroにマウス、キーボード、モニタをつないだ状態)

  特に考えることはなく、作成したMicro SD カードを挿入して起動します。

  ・初回起動時のチュートリアルに従ってロケール、パスワード変更、Wifi接続の設定

  ・RaspberryPIの設定から、ホスト名の変更、カメラ、SSHの有効化(VNCは無効で)

  ・画面右上のWifiアイコンを右クリック、IPアドレスを固定化

  ここまではディスプレイとマウス、キーボードを接続して行います。

  Micro SD カードを作成、Windowsにてその内容を書き換えることもできますが、ここでは触れません。

3.OSセットアップ(WindowsPCからSSH接続)

  ここまでできれば、ディスプレイ、マウス、キーボードを接続する必要はありません。

  WindowsなどからSSH接続で操作できます。

  ・アップデート、アップグレードの実施(1つ前のイメージなので時間はかかる)

  $sudo apt update

  $sudo apt full-upgrade

  ・ディスプレイドライバをインストール

www.waveshare.com

 

ここまでできれば、最低限、カメラから録画している動画をディスプレイに表示、録画する準備ができました。

Raspberry Pi ドライブレコーダーの作成②

ドライブレコーダーの作成(開発)を進めるにあたり、最低限の物品購入をします。

 

Raspberry Pi Zero

  最もコンパクト、安価な本体です。

  本来は高性能なほかの機種(4とか)を選択したいのですが、今回はこれで進めます。

  以前に比べ、購入しずらくなった印象ですが、どうなんでしょうね…。

Raspberry Pi Zero 用のケース

  お好みのものでよいと思いますが、なるべくシンプルなほうが良いと思います。

 ・Raspberry Pi Zero 対応カメラ

  ほかのRaspberry Pi とケーブルが違うのでご注意。

  通常のRaspberry Pi用のカメラを、Zero用のケーブルで接続しました。

  カメラのケースもあったほうが良いと思います。

・Paspberry Pi LCDディスプレイ

  タッチスクリーン機能が付いていますが、今回は使用しません。ただ、画像を映すだけです。

  安価で手軽な分描画スピードが遅いです。録画しながら画面表示しますが、あくまでおまけ程度です。

Raspberry Pi ドライブレコーダーの作成①

我が家、新車購入することになり、いろいろとお金がかかります。

私が住んでいる地域は雪国なので、冬用の装備(ワイパーやスタッドレスタイヤ)への出費もかさみそうです。

(そもそも、雪国でなければ4WD・AWDではなくFFで済むんでしょうけど)

私の住んでいる地域でのドライブレコーダー装着率、知人に聞く限りはほぼゼロ。

でも、YouTubeなんかで見る事故動画なんかでは、やっぱあったほうがいいんだろうな、と思うところです。

 

ということで、今回は「手持ちの機器(raspberry pi)でドライブレコーダー(もどき)を作成しよう」ということで進めていこうかと思います。

 

新車!ということでモチベーションMAX!ではあるのですが、やはり、出費は抑えたい。

ですので、今回、以下のポリシーの元、進めていきます。

 

・できる限り、低価格。

  中華製は除いて、日本メーカー、comtecあたりと比較して、15,000円以内を目指します。

・できる限り、コンパクト。

  大きくて性能の良いハードは使用せず、コンパクト・低価格なハードウェア構成を目指します。

・拡張性。

  とりあえず、「録画する」を目指しますが、手間とお金をかければGPSなどの拡張性も視野に入れます(難しいかもしれませんが)

・携帯性。

  電源(USB電源)さえあれば、他人の車でも、自転車でも、徒歩でも録画ができること。

・多少の不便さは気にしない。

  専用ハードウェアではないので、多少不便でも気にしません。

  例えば、大きなショックがあった前後で録画を保持するとか、起動の早さとか。

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

在宅ワークをしばらくすることになり、仕事部屋の、私用と仕事用の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()