Raspberry Pi 電子工作 12:LCDディスプレイに現在日時、温度・湿度、CPU・メモリ状態、勤怠状態(?)を表示

前々回の、距離センサーを用いた着席状態の検出から、人感センサーを使用した検出に変更しています。

距離センサーでの検出では、センサーの前に障害物を「ドン」と置いておくだけで検出されますが、人感センサーでは動いていないと検出されないので、「ジッと考えている、10分以上」ではない限り、検出精度は上がったかと思います。

 

 

1. 仕様

  前回と、基本的には同じですが、「動いている」という要素が追加されます。

  情報を1秒ごとにLCDディスプレイに表示します。

  ・現在の日時

  ・温度・湿度

  ・CPU温度、使用率・メモリの使用量、総量

  ・センサーで動いているか(動作状態)

 

  動作状態については、センサーをデスクに座った際に反応する位置に設置し、以下の条件で働いている、いないを判定、表示します。

  (デスクでジッと考えているのを働いていないとみなすのが正しいとは思っていませんが、あくまで目安ということで)

  ・働いている状態

    一定間隔で動いている場合

  ・働いていない状態

    動いていない状態が10分(600秒)以上続いた場合

2. 配線図

  距離センサーより端子が1つ減ったので、ちょっと簡単に。

f:id:kobatom5278:20210211225600p:plain

 

3. Pythonプログラミング

  人感センサーの検知結果がboolになりました。

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

# 使用するライブラリのインポート
import time
from time import sleep
import smbus
import datetime
import subprocess
import re
import psutil
import RPi.GPIO as GPIO

# 定数の定義
# LCDディスプレイのI2Cのアドレス
LCD_I2C_ADDR = 0x27
# LCDの1行目のアドレス
LCD_LINE_1 = 0x80
# LCDの2行目のアドレス
LCD_LINE_2 = 0xC0
# 1行あたりの文字数
LCD_WIDTH = 16
# バックライトのON/OFF(ON:0X08, OFF:0X00を指定)
LCD_BACKLIGHT = 0X08
LCD_CHR = 1
LCD_CMD = 0
ENABLE = 0b00000100
E_PULSE = 0.0005
E_DELAY = 0.0005
# HC-SR04のGPIOピン番号
PIN_TRIG = 24
PIN_ECHO = 25
# 人感センサーのGPIOピン番号
ZINKAN_PIN = 23
# 音速(cm / s)
SPEED_OF_SOUND = 33145

def init_lcd():
    """
    LCDの初期化処理
    """
    lcd_byte(0x33, LCD_CMD)
    lcd_byte(0x32, LCD_CMD)
    lcd_byte(0x06, LCD_CMD)
    lcd_byte(0x0C, LCD_CMD)
    lcd_byte(0x28, LCD_CMD)
    lcd_byte(0x01, LCD_CMD)
    sleep(E_DELAY)

def lcd_byte(bits, mode):
    """
    LCDへの書き込み2
    """
    bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT
    bits_low =  mode | ((bits << 4) & 0xF0) | LCD_BACKLIGHT
    bus.write_byte(LCD_I2C_ADDR, bits_high)
    lcd_toggle_enable(bits_high)
    bus.write_byte(LCD_I2C_ADDR, bits_low)
    lcd_toggle_enable(bits_low)

def lcd_toggle_enable(bits):
    """
    LCDへの書き込みサブ
    """
    sleep(E_DELAY)
    bus.write_byte(LCD_I2C_ADDR, (bits | ENABLE))
    sleep(E_PULSE)
    bus.write_byte(LCD_I2C_ADDR, (bits & ~ENABLE))
    sleep(E_DELAY)

def lcd_string(message: str, dispLine):
    """
    LCDへの文字の表示(半角文字列のみ)
    """
    # 表示対象行の初期化
    lcd_byte(dispLine, LCD_CMD)
    # 文字列を1行あたりの文字数に調整
    message = message.ljust(LCD_WIDTH)
    # 表示処理
    for dispIndex in range(LCD_WIDTH):
        lcd_byte(ord(message[dispIndex]), LCD_CHR)

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

# 使用するライブラリのインポート
import smbus
from time import sleep

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

def cpu_temp():
    """
    CPU温度の取得
    """
    f = open("/sys/class/thermal/thermal_zone0/temp","r")
    cpu_temp = 0
    for t in f:
        cpu_temp = t[:2]
        # 小数点以下2桁まで表示する場合
        # cpu_temp = t[:2]+"."+t[2:3]
    f.close()
    return cpu_temp

def cpu_temp_command():
    """
    CPU温度の取得(コマンドで取得)
    """
    proc = subprocess.run(["vcgencmd", "measure_temp"], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
    cpu_temp = re.split("[=\']", proc.stdout.decode("utf8"))[1]
    return float(cpu_temp)

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

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)

def init_GPIO():
    """
    GPIOの初期設定
    """
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)

    GPIO.setup(PIN_TRIG, GPIO.OUT)
    GPIO.setup(PIN_ECHO, GPIO.IN)
    GPIO.setup(ZINKAN_PIN, GPIO.IN)

def zinkan():
    """
    人感センサーの検知結果を返却
    """
    if GPIO.input(ZINKAN_PIN):
        return True
    else:
        return False

# I2Cの取得
bus = smbus.SMBus(1)

try:
    # LCD初期化
    init_lcd()
    # GPIOの初期化
    init_GPIO()

    # 無限ループ、各情報を1秒間隔で表示
    dispMode = 0
    # 最終動作時間(人感センサーが「検知」だった日時、時間)
    workLastTime = None

    while True:
        if (dispMode == 0):
            # 「年月日と時分秒」を表示
            now = datetime.datetime.now()
            lcd_string(f'date: {now.strftime("%Y/%m/%d")}' , LCD_LINE_1)
            lcd_string(f'time: {now.strftime("%H:%M:%S")}', LCD_LINE_2)

            # 次の処理が1秒かかるので、スリープしない
            dispMode = 1
        elif (dispMode == 1):
            # 「温度と湿度」を表示
            templature , humidity = tempelature_humidity()
            lcd_string(f'temp: {str(templature)[:4]}\337C', LCD_LINE_1)
            lcd_string(f'hum : {str(humidity)[:4]}%', LCD_LINE_2)

            # 次の処理が1秒かかるので、スリープしない
            dispMode = 2
        elif (dispMode == 2):
            # 「CPU温度」「CPU使用率」と「メモリ情報」を表示
            mem_info = memory_info()
            lcd_string(f'cpu : {cpu_temp()}\337C {cpu_percent()}%', LCD_LINE_1)
            lcd_string(f'mem : {mem_info[0]}/{mem_info[1]}MB', LCD_LINE_2)

            sleep(1)
            dispMode = 3
        elif (dispMode == 3):
            # 「人感センサー」の検知結果を取得
            isZinkan = zinkan()
            # 人感センサーの検知結果が「検知」の場合、最終動作時間を更新
            if isZinkan:
                workLastTime = datetime.datetime.now()
            # 直近の最終動作時間から10分(600秒)未満の場合は、働いているとみなす
            working = False
            if workLastTime != None and (datetime.datetime.now() - workLastTime).seconds < 600:
                working = True
            # 働いている、いないにより表示を切り替える
            line1 = ''            
            if working == False:
                if workLastTime == None:
                    # 働いておらず、最終動作時間が未設定(初期状態)
                    line1 = 'Squeeze !!'
                else:
                    # 働いておらず、最終動作時間から時間がたっている
                    line1 = f'Squeeze:{workLastTime.strftime("%H:%M:%S")}'
            else:
                # 働いている
                line1 = f'work: {workLastTime.strftime("%H:%M:%S")}'
            # 表示する
            #lcd_string(f'dist: {distance: >7} cm', LCD_LINE_1)
            lcd_string(line1, LCD_LINE_1)
            lcd_string('', LCD_LINE_2)
            
            sleep(1)
            dispMode = 0

finally:
    lcd_byte(0x01, LCD_CMD)